#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
//第十讲：操作符详解+第十一讲：深入理解指针（1）
//9. 结构成员访问操作符
//9.1 结构体(自定义类型)：用来描述复杂对象
//这里面可以包含多个属性
//9.1.1 结构的声明+9.1.2 结构体变量的定义和初始化
//struct student
//{
//	char name[20];
//	int age;
//	int high;
//	float wight;
//}s4,s5,s6;//s4,s5,s6是结构体变量(全局的)
//int main()
//{
//	//结构体变量的创建:
//	struct student s1 = {"张三",20,180,75};//初始化(局部的)
//	struct student s2 = { .age = 30,.name = "李四",.wight = 60,.high = 170 };//不按照顺序初始化
//	//struct student s3;
//	return 0;
//}

//结构体里包含结构体如何初始化:
//struct A
//{
//	char c;
//};
//struct B
//{
//	struct A c;//A是类型，c是变量名字
//};
//int main()
//{
//	struct B b = { {"hehe"} };//只需要再套一个大括号即可
//	return 0;
//}


//9.2 结构成员访问操作符
//9.2.1 结构体成员的直接访问
//结构体成员的直接访问是通过点操作符（.）访问的。点操作符接受两个操作数
//struct student
//{
//	char name[20];
//	int age;
//	int high;
//	float wight;
//};
//int main()
//{
//	struct student s1 = {"张三",20,180,75.5f};//初始化(局部的)
//	printf("%s %d %d %.2f", s1.name, s1.age, s1.high, s1.wight);
//	return 0;
//}

//9.2.2需要指针的知识，后期再学

//10. 操作符的属性：优先级、结合性
//C语⾔的操作符有2个重要的属性：优先级、结合性，这两个属性决定了表达式求值的计算顺序。
//10.1 优先级
//优先级指的是，如果⼀个表达式包含多个运算符，哪个运算符应该优先执⾏。各种运算符的优先级是
//不⼀样的。
//10.2 结合性
//如果两个运算符优先级相同，优先级没办法确定先计算哪个了，这时候就看结合性了，则根据运算符
//是左结合，还是右结合，决定执⾏顺序。



//11. 表达式求值
//11.1 整型提升
//C语⾔中整型算术运算总是⾄少以缺省（默认）整型类型的精度来进⾏的。
//为了获得这个精度，表达式中的字符和短整型操作数在使⽤之前被转换为普通整型，这种转换称为整型提升。
//如何进⾏整体提升呢？
//1. 有符号整数提升是按照变量的数据类型的符号位来提升的
//2. ⽆符号整数提升，⾼位补0
//int main()
//{
//	char a = 3;//char -- signed char
	//00000011 char a(补码)
	//整型提升:
	//00000000 00000000 00000000 00000011 提升后（补码）(按照最高位符号位来补0或1)

//	char b = 127;
	//01111111 char b(补码)
	//整型提升:
	//00000000 00000000 00000000 01111111

	//char c = a + b;
	//a和b的值被提升为普通整型，然后再执⾏加法运算。
	//加法运算完成之后，结果将被截断，然后再存储于c中。
	//a: 00000000 00000000 00000000 00000011
	//b: 00000000 00000000 00000000 01111111
	//   00000000 00000000 00000000 10000010 (加后)
	//截断放在c中:10000010(因为c是char类型，只有一个字节)

//	printf("%d", c);
	//%d -- 以十进制的方式打印有符号的整数（打印原码）
	// 11111111 11111111 11111111 10000010 - c提升后的结果（补码）
	// 10000000 00000000 00000000 01111110 - c提升后的结果（原码）

	//char和short类型的值需要整型提升(因为他们的所占字节小于int)
	//return 0;
//}


//11.2 算术转换（字节少的向字节多的转换）
//如果某个操作符的各个操作数属于不同的类型，那么除⾮其中⼀个操作数的转换为另⼀个操作数的类
//型，否则操作就⽆法进⾏。下⾯的层次体系称为寻常算术转换。
//例如:
//int和double计算，int要转换为double
//short和double计算，short要先整型提升变为int，再算数转换为double


//11.3 问题表达式解析
//11.3.1 表达式1:
//a*b + c*d + e*f
 //无法确定唯一的计算路径1.可以先算前两个乘再算第一个加 2.可以先算所有的乘，再算加
//解决方法:1.加括号 2.拆开写

//11.3.2 表达式2
//c + --c
//操作符的优先级只能决定⾃减 -- 的运算在 + 的运算的前⾯，但是我们并没有办法得知， + 操
//作符的左操作数的获取在右操作数之前还是之后求值
//假设c=5  左边的c可能是5也可能是4，有歧义

//11.3.3 表达式3
//int main()
//{
//	int i = 10;
//	i = i-- - --i * (i = -3) * i++ + ++i;
//	printf("i = %d\n", i);
//	return 0;
//}（不需要去理解，感受就好了）

//11.3.4 表达式4
//int fun()
//{
//	static int count = 1;
//	return ++count;
//}
//int main()
//{
//	int answer;
//	answer = fun() - fun() * fun();
//	printf("%d\n", answer);//输出多少？
//	return 0;//我们只能确定先算乘法后算减法，但是不知道先调用哪一个函数，这段代码也是有歧义的
//}

//11.3.5 表达式5:
//int main()
//{
//	int i = 1;
//	int ret = (++i) + (++i) + (++i);//存在多种计算路径
//	printf("%d\n", ret);
//	printf("%d\n", i);
//	return 0;
//}

//11.4 总结
//即使有了操作符的优先级和结合性，我们写出的表达式依然有可能不能通过操作符的属性确定唯⼀的
//计算路径，那这个表达式就是存在潜在⻛险的，建议不要写出特别复杂的表达式。



//第11讲：深⼊理解指针(1)
//1. 内存和地址
//1.1 内存
//内存划分为⼀个个的内存单元，每个内存单元的⼤⼩取1个字节。
//其中，每个内存单元，相当于⼀个学⽣宿舍，⼀个字节空间⾥⾯能放8个⽐特位，就好⽐同学们住的⼋⼈间，每个⼈是⼀个⽐特位。
//每个内存单元也都有⼀个编号（这个编号就相当于宿舍房间的⻔牌号），有了这个内存单元的编号，CPU就可以快速找到⼀个内存空间。
//C语⾔中给地址起了新的名字叫：指针。
//所以我们可以理解为：
//内存单元的编号 == 地址 == 指针


//1.2 究竟该如何理解编址	
//我们可以简单理解，32位机器有32根地址总线，每根线只有两态，表⽰0, 1【电脉冲有⽆】，那么
//⼀根线，就能表⽰2种含义，2根线就能表⽰4种含义，依次类推。32根地址线，就能表⽰2 ^ 32种含
//义，每⼀种含义都代表⼀个地址。



//2. 指针变量和地址
//2.1 取地址操作符（ & ）2.2 指针变量和解引⽤操作符（*）
int main()
{
	//int a = 10;
	//&a --- &取地址操作符(取a的地址)
	//int* p = &a;//p是一个变量（指针变量），是一块空间
	//printf("%p\n", p);//取出的是较小的地址(第一个字节的地址)
	//*在说明p是指针变量
	//int说明p指向的是变量是int类型
	//指针 -- 地址
	//指针变量 -- 存放地址的变量

	//2.2.3 解引⽤操作符（间接访问操作符）
	//*p = 1;
	//printf("%d\n", a);
	//或者:
	//*&a = 1;//有点脱裤子放屁的意思


	//2.3 指针变量的⼤⼩(地址总线说了算————>地址)
	//32位机器假设有32根地址总线，每根地址线出来的电信号转换成数字信号后
	//是1或者0，那我们把32根地址线产⽣的2进制序列当做⼀个地址，那么⼀个地址就是32个bit位，需要4个字节才能存储。
	//这32个bit位相当于32个地址总线，因为32个bit位可以表示2^32种结果
	//printf("%zd\n", sizeof(char*));
	//printf("%zd\n", sizeof(int*));
	//printf("%zd\n", sizeof(long*));
	//printf("%zd\n", sizeof(float*));
	//printf("%zd\n", sizeof(double*));
	//printf("%zd\n", sizeof(short*));



	//3. 指针变量类型的意义
	//	指针变量的⼤⼩和类型⽆关，只要是指针变量，在同⼀个平台下，⼤⼩都是⼀样的，为什么还要有各
	//	种各样的指针类型呢？
	// 3.1 指针的解引⽤
	//int n = 0x11223344;
	//int* pi = &n;
	//*pi = 0;//n的4个字节全变为00(十六进制)
	//-------------------------------------
	//int n = 0x11223344;
	//char* pc = (char*)&n;
	//*pc = 0;//n的第一个字节变成00(十六进制)
	//对比，主要在调试时观察内存的变化。
	//指针的类型决定了指针进行解引用操作符的时候访问几个字节,也就是决定指针的权限
	//char*类型的指针只能访问(修改)一个字节，int*类型的指针可以访问(修改)4个字节


	//3.2 指针+-整数
	//int a = 10;
	//int* pa = &a;
	//char* pc = &a;
	//printf("pa     = %p\n", pa);
	//printf("pa + 1 = %p\n", pa+1);//向后走四个字节
	//printf("pc     = %p\n", pc);
	//printf("pa + 1 = %p\n", pc+1);//向后走一个字节
	//指针类型决定了指针进行+1，-1的时候，一次走几个字节
	return 0;
}